package com.cyanite.playlv2;

import com.cyanite.playlv2.error.LocationException;
import com.cyanite.playlv2.location.BestLocationListener;
import com.cyanite.playlv2.preferences.Preferences;
import com.cyanite.playlv2.util.NullDiskCache;
import com.cyanite.playlv2.util.RemoteResourceManager;
import com.cyanite.playlv2.widget.DataBaseAdapter;
import com.cyanite.playlv2api.PlayLv2Api;
import com.cyanite.playlv2api.util.IconUtils;

import android.app.Application;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.location.Location;
import android.location.LocationManager;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.preference.PreferenceManager;
import android.text.TextUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.Observer;

public class PlayLv2 extends Application {

    public static final String PACKAGE_NAME = "com.cyanite.playlv2";
    // mVersion that using for User-Agent
    public static final String PACKAGE_NAME_V = "cyanite_playlv2_android_v";

    public static final String INTENT_ACTION_LOGGED_OUT = "com.cyanite.playlv2.intent.action.LOGGED_OUT";
    public static final String INTENT_ACTION_LOGGED_IN = "com.cyanite.playlv2.intent.action.LOGGED_IN";
    // shortCut
    private static final String ACTION_INSTALL_SHORTCUT = "com.android.launcher.action.INSTALL_SHORTCUT";
    private static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate";
    private String mVersion = null;

    private TaskHandler mTaskHandler;
    private HandlerThread mTaskThread;

    private SharedPreferences mPrefs;
    private RemoteResourceManager mRemoteResourceManager;

    private PlayLv2Api mPlayLv2Api;
    private BestLocationListener mBestLocationListener = new BestLocationListener();
    private boolean mIsFirstRun;
    private ArrayList<String> mDBlist;

    @Override
    public void onCreate() {
        mVersion = getVersionString(this);

        // Check if this is a new install by seeing if our preference file exists on disk.
        mIsFirstRun = checkIfIsFirstRun();
        if ( mIsFirstRun )  {
            createShortcut();
        }

        // Setup Prefs (to load dumpcatcher)
        mPrefs = PreferenceManager.getDefaultSharedPreferences(this);

        // Setup some defaults in our preferences if not set yet.
        Preferences.setupDefaults(mPrefs, getResources());

        // If we're on a high density device, request higher res images. This singleton
        // is picked up by the parsers to replace their icon urls with high res versions.
        float screenDensity = getApplicationContext().getResources().getDisplayMetrics().density;
        IconUtils.get().setRequestHighDensityIcons(screenDensity > 1.0f);

        // Sometimes we want the application to do some work on behalf of the
        // Activity. Lets do that Asynchronously.
        mTaskThread = new HandlerThread("Playlv2 -AsyncThread");
        mTaskThread.start();
        mTaskHandler = new TaskHandler(mTaskThread.getLooper());

        // Set up storage cache.
        loadResourceManagers();

        // Catch sdcard state changes
        new MediaCardStateBroadcastReceiver().register();

        // Catch logins or logouts.
        new LoggedInOutBroadcastReceiver().register();

        // Log into PlayLv2, if we can.
        loadPlayLv2();

        // Initalize DB or creating default DB
        initDataBase();
    }

    public boolean isReady() {
        return getPlayLv2Api().hasLoginAndPassword() && !TextUtils.isEmpty(getUserId());
    }
    
    public boolean isFirstRun() {
        return mIsFirstRun;
    }
    
    public void setIsFristRun(boolean isFirstRun) {
        mIsFirstRun = isFirstRun;
    }

    public PlayLv2Api getPlayLv2Api() {
        return mPlayLv2Api;
    }

    public SharedPreferences getPrefs() {
        return mPrefs;
    }

    public String getUserId() {
        return Preferences.getUserId(mPrefs);
    }

    public String getUserName() {
        return Preferences.getUserName(mPrefs);
    }

    public String getUserEmail() {
        return Preferences.getUserEmail(mPrefs);
    }

    public String getUserGender() {
        return Preferences.getUserGender(mPrefs);
    }

    public String getVersion() {
        if (mVersion != null) {
            return mVersion;
        } else {
            return "";
        }
    }

    public String getLastSeenChangelogVersion() {
        return Preferences.getLastSeenChangelogVersion(mPrefs);
    }

    public void storeLastSeenChangelogVersion(String version) {
        Preferences.storeLastSeenChangelogVersion(mPrefs.edit(), version);
    }

    public boolean getUseNativeImageViewerForFullScreenImages() {
        return Preferences.getUseNativeImageViewerForFullScreenImages(mPrefs);
    }

    public RemoteResourceManager getRemoteResourceManager() {
        return mRemoteResourceManager;
    }

    public BestLocationListener requestLocationUpdates(boolean gps) {
        mBestLocationListener.register(
                (LocationManager) getSystemService(Context.LOCATION_SERVICE), gps);
        return mBestLocationListener;
    }

    public BestLocationListener requestLocationUpdates(Observer observer) {
        mBestLocationListener.addObserver(observer);
        mBestLocationListener.register(
                (LocationManager) getSystemService(Context.LOCATION_SERVICE), true);
        return mBestLocationListener;
    }

    public void removeLocationUpdates() {
        mBestLocationListener
                .unregister((LocationManager) getSystemService(Context.LOCATION_SERVICE));
    }

    public void removeLocationUpdates(Observer observer) {
        mBestLocationListener.deleteObserver(observer);
        this.removeLocationUpdates();
    }

    public Location getLastKnownLocation() {
        return mBestLocationListener.getLastKnownLocation();
    }

    public Location getLastKnownLocationOrThrow() throws LocationException {
        Location location = mBestLocationListener.getLastKnownLocation();
        if (location == null) {
            throw new LocationException();
        }
        return location;
    }

    public void clearLastKnownLocation() {
        mBestLocationListener.clearLastKnownLocation();
    }

    public void requestUpdateUser() {
        mTaskHandler.sendEmptyMessage(TaskHandler.MESSAGE_UPDATE_USER);
    }

    private void loadPlayLv2() {
        mPlayLv2Api = new PlayLv2Api(PlayLv2Api.createHttpApi(mVersion));
        String phoneNumber = mPrefs.getString(Preferences.PREFERENCE_LOGIN, null);
        String password = mPrefs.getString(Preferences.PREFERENCE_PASSWORD, null);

        mPlayLv2Api.setCredentials(phoneNumber, password);
        if (mPlayLv2Api.hasLoginAndPassword()) {
            sendBroadcast(new Intent(INTENT_ACTION_LOGGED_IN));
        } else {
            sendBroadcast(new Intent(INTENT_ACTION_LOGGED_OUT));
        }
    }

    /**
     * Provides static access to a PlayLv2Api mInstance. This mInstance is
     * initiated without user credentials.
     *
     * @param context the context to use when constructing the PlayLv2Api
     *            mInstance
     * @return the PlayLv2Api instace
     */
    public static PlayLv2Api createPlayLv2Api(Context context) {
        String version = getVersionString(context);
        return new PlayLv2Api(PlayLv2Api.createHttpApi(version));
    }

    /**
     * Constructs the version string of the application.
     *
     * @param context the context to use for getting package info
     * @return the versions string of the application
     */
    private static String getVersionString(Context context) {
        // Get a version string for the app.
        try {
            PackageManager pm = context.getPackageManager();
            PackageInfo pi = pm.getPackageInfo(PACKAGE_NAME, 0);
            return PACKAGE_NAME_V + String.valueOf(pi.versionCode);
        } catch (NameNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    private void loadResourceManagers() {
        // We probably don't have SD card access if we get an
        // IllegalStateException. If it did, lets
        // at least have some sort of disk cache so that things don't npe when
        // trying to access the resource managers.
        try {
            mRemoteResourceManager = new RemoteResourceManager("cache");
        } catch (IllegalStateException e) {
            mRemoteResourceManager = new RemoteResourceManager(new NullDiskCache());
        }
    }

    public boolean getIsFirstRun() {
        return mIsFirstRun;
    }

    private boolean checkIfIsFirstRun() {
        File file = new File(
            "/data/data/com.cyanite.playlv2/shared_prefs/com.cyanite.playlv2_preferences.xml");
        return !file.exists();
    }

    private void initDataBase()  {
        DataBaseAdapter dbAdapter = new DataBaseAdapter(this);
        dbAdapter.open();
        dbAdapter.close();
        dbAdapter.initDataBase();
    }

    public ArrayList<String> getDBList()   {
        return mDBlist;
    }

    public void setDBList(ArrayList<String> list)   {
        mDBlist = list;
    }

    private void createShortcut() {
        Intent shortcutIntent = new Intent(ACTION_INSTALL_SHORTCUT);
        shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME,
                getString(R.string.app_name));
        shortcutIntent.putExtra(EXTRA_SHORTCUT_DUPLICATE, false);
        Intent intent = new Intent();
        intent.setComponent(new ComponentName(this.getPackageName(),
                ".MainActivity"));
        shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, intent);
        shortcutIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
                Intent.ShortcutIconResource.fromContext(this,
                        R.drawable.icon));
        sendBroadcast(shortcutIntent);
    }

    /**
     * Set up resource managers on the application depending on SD card state.
     *
     */
    private class MediaCardStateBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (Intent.ACTION_MEDIA_UNMOUNTED.equals(intent.getAction())) {
                getRemoteResourceManager().shutdown();
                loadResourceManagers();
            } else if (Intent.ACTION_MEDIA_MOUNTED.equals(intent.getAction())) {
                loadResourceManagers();
            }
        }

        public void register() {
            // Register our media card broadcast receiver so we can
            // enable/disable the cache as
            // appropriate.
            IntentFilter intentFilter = new IntentFilter();
            intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
            intentFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);
            // intentFilter.addAction(Intent.ACTION_MEDIA_REMOVED);
            // intentFilter.addAction(Intent.ACTION_MEDIA_SHARED);
            // intentFilter.addAction(Intent.ACTION_MEDIA_BAD_REMOVAL);
            // intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTABLE);
            // intentFilter.addAction(Intent.ACTION_MEDIA_NOFS);
            // intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);
            // intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
            intentFilter.addDataScheme("file");
            registerReceiver(this, intentFilter);
        }
    }

    private class LoggedInOutBroadcastReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            if (INTENT_ACTION_LOGGED_IN.equals(intent.getAction())) {
                requestUpdateUser();
            }
        }

        public void register() {
            // Register our media card broadcast receiver so we can
            // enable/disable the cache as
            // appropriate.
            IntentFilter intentFilter = new IntentFilter();
            intentFilter.addAction(INTENT_ACTION_LOGGED_IN);
            intentFilter.addAction(INTENT_ACTION_LOGGED_OUT);
            registerReceiver(this, intentFilter);
        }
    }

    private class TaskHandler extends Handler {

        private static final int MESSAGE_UPDATE_USER = 1;

        public TaskHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            switch (msg.what) {
                case MESSAGE_UPDATE_USER:
                    return;
            }
        }
    }
}
